# coding=utf-8
# -*- coding: UTF-8 -*-
#
# This file is part of the kernelstudio package.
#
# (c) 2014-2025 zlin <admin@kernelstudio.com>
#
# For the full copyright and license information, please view the LICENSE file
# that was distributed with this source code.

import logging
import os
import subprocess
from abc import ABC, abstractmethod
from pathlib import Path
from typing import Union, Optional

from ksai.document.context import DocumentException

logger = logging.getLogger(__name__)


class DocumentConverterException(DocumentException):
    pass


class DocumentConverter(ABC):

    @abstractmethod
    def convert(self, source_filepath: str | Path, dest_filepath: str | Path) -> bool:
        raise NotImplementedError


class LibOfficeDocumentConverter(DocumentConverter):
    """使用 LibreOffice 进行文档格式转换的高级工具类"""

    # 常见 LibreOffice 安装路径
    COMMON_PATHS = [
        # Linux 路径
        "/usr/bin/libreoffice",
        "/usr/bin/soffice",
        "/usr/local/bin/libreoffice",
        # macOS 路径
        "/Applications/LibreOffice.app/Contents/MacOS/soffice",
        # Windows 路径
        "C:\\Program Files\\LibreOffice\\program\\soffice.exe",
        "C:\\Program Files (x86)\\LibreOffice\\program\\soffice.exe"
    ]

    def __init__(self):
        self.executable = self.find_executable()

        if not self.executable:
            raise EnvironmentError("LibreOffice executable not found. Please install LibreOffice "
                                   "or set LIB_OFFICE_EXECUTABLE environment variable.")

        logger.debug(f"Using LibreOffice executable: {self.executable}")

    @classmethod
    def find_executable(cls) -> Optional[str]:
        """查找 LibreOffice 可执行文件路径"""
        # 1. 检查环境变量
        if env_path := os.getenv('LIB_OFFICE_EXECUTABLE'):
            if Path(env_path).is_file():
                return env_path
            logging.warning(f"Environment variable LIB_OFFICE_EXECUTABLE points to non-existent file: {env_path}")

        # 2. 检查常见安装路径
        for path in cls.COMMON_PATHS:
            if Path(path).is_file():
                return path

        # 3. 尝试在系统 PATH 中查找
        for exe_name in ['libreoffice', 'soffice', 'soffice.exe']:
            try:
                found_path = subprocess.check_output(['which', exe_name],
                                                     stderr=subprocess.STDOUT).decode().strip()
                if Path(found_path).is_file():
                    return found_path
            except (subprocess.CalledProcessError, FileNotFoundError):
                pass

        logging.error("LibreOffice executable not found. Please install LibreOffice.")
        return None

    def convert(self,
                source_filepath: Union[str, Path],
                dest_filepath: Union[str, Path],
                timeout: int = 120) -> bool:
        """
        将文档从一种格式转换为另一种格式

        :param source_filepath: 源文件路径
        :param dest_filepath: 目标文件路径
        :param timeout: 转换超时时间（秒）
        :return: 转换是否成功
        """
        # 规范化路径为 Path 对象
        source_path = Path(source_filepath)
        dest_path = Path(dest_filepath)

        # 验证源文件
        if not source_path.is_file():
            logger.error(f"Source file not found: {source_path}")
            raise FileNotFoundError(f"Source file not found: {source_path}")

        # 准备目标目录
        dest_dir = dest_path.parent
        if not dest_dir.exists():
            logger.info(f"Creating target directory: {dest_dir}")
            dest_dir.mkdir(parents=True, exist_ok=True)

        # 获取目标文件后缀（格式）
        target_suffix = dest_path.suffix.lstrip('.')
        if not target_suffix:
            logger.error(f"Invalid target file extension: {dest_path}")
            raise ValueError(f"Target file must have an extension: {dest_path}")

        # 构建转换命令
        command = [
            str(self.executable),
            '--headless',  # 无界面模式
            '--norestore',  # 不恢复上次会话
            '--nodefault',  # 不使用默认模板
            '--nologo',  # 不显示启动画面
            '--nofirststartwizard',  # 跳过首次启动向导
            '--convert-to', target_suffix,
            '--outdir', str(dest_dir),
            str(source_path)
        ]

        logger.info(f"Starting conversion: {source_path} → {dest_path}")
        logger.debug(f"Conversion command: {' '.join(command)}")

        try:
            # 执行转换命令
            result = subprocess.run(
                command,
                check=True,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                timeout=timeout
            )

            # 记录 LibreOffice 输出（调试用）
            if result.stdout:
                logger.debug(f"LibreOffice stdout: {result.stdout.decode('utf-8', errors='ignore')}")
            if result.stderr:
                logger.debug(f"LibreOffice stderr: {result.stderr.decode('utf-8', errors='ignore')}")

            # 验证目标文件是否创建
            if not dest_path.exists():
                logger.error(f"Conversion completed but target file not found: {dest_path}")
                return False

            logger.info(f"Successfully converted to: {dest_path}")
            return True

        except subprocess.CalledProcessError as e:
            error_msg = f"Conversion failed with exit code {e.returncode}"
            if e.stderr:
                error_msg += f"\nError output: {e.stderr.decode('utf-8', errors='ignore')}"
            logger.error(error_msg)
            return False

        except subprocess.TimeoutExpired:
            logger.error(f"Conversion timed out after {timeout} seconds")
            return False

        except Exception as e:
            logger.exception(f"Unexpected error during conversion: {str(e)}")
            return False


document_converter = LibOfficeDocumentConverter()
